chore(spec): re-grade ActionSchema operation / patch to live in the liveness ledger - #16784
Conversation
…tion-liveness-flip
…action declarative update
📓 Docs Drift Check
What this run could not see
Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
ACCEPT — head
|
| leg | mutation | result |
|---|---|---|
| baseline | none | 27 / 27 pass |
operation |
isDeclarativeUpdateAction returns false |
24 of 27 fail |
patch |
declarativeUpdateWrite stops reading action.patch |
16 of 27 fail |
Each leg was mutated on disk, verified by blob hash against HEAD, restored, and the restoration proved by an empty git diff HEAD and a matching blob hash. Neither row is a paper flip. Counts move exactly as pinned: action live 41 → 43, planned 4 → 2; totals 848 / 12 → 850 / 10, with state-counts.md regenerated by check:generated --fix rather than hand-edited.
Also correctly refused: check:dual-build-cjs-loads exit 3 recorded as NOT MEASURED, quoting the gate's own ⛔ This is NOT a pass: nothing was measured, instead of being counted green.
Actions taken
ACCEPT → ready → enqueue, in that order. Fixes #15080 closes the card on merge; ⛔ this seat does not merge it by hand and does not bypass the queue.
Two acceptance notes carried forward, neither blocking: the card's claim that check:generated --fix also refreshes the references pages is overstated (only state-counts.md was stale; the docs generator does not read the liveness ledger), and liveness/README.md's hand-written "Current state" note for action is left to the next action-ledger PR to avoid colliding with the wave sibling writing the same directory.
https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
Generated by Claude Code
…ed-locale-axis Second sync round for PR #16592. Sole conflict was the generated total row of packages/spec/liveness/state-counts.md, where main's #16784 re-grade of ActionSchema operation/patch (planned -> live) met this branch's added experimental seed.locale row. Resolved by taking either side of the anchor numbers; the table is regenerated from the merged ledger in the follow-up commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6
`gen:liveness-counts` recomputed from the merged ledger: the only stale artifact this round. The total row is the union of both sides — main's #16784 re-grade of ActionSchema operation/patch (planned -> live, 848 -> 850) plus this branch's experimental `seed.locale` row (exp 5 -> 6, classified 958 -> 959). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6
…ets, composed with env (objectstack-ai#16592) * feat(spec, metadata-protocol): add a locale axis to seed datasets WIP — schema + loader halves; tests, docs and changeset to follow. Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf Co-authored-by: Claude <noreply@anthropic.com> * test(seed): pin the locale axis; docs, liveness ledger and changeset Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf Co-authored-by: Claude <noreply@anthropic.com> * chore(spec): regenerate reference docs, skill refs and liveness counts Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf Co-authored-by: Claude <noreply@anthropic.com> * chore: re-anchor system-context census lines and pin the new engine doubles Both are mechanical, gate-driven repairs of this change's own side effects: the +51 lines in seed-loader.ts moved three cited anchors, and the new locale-scope test pins engine doubles the contract ledger had not recorded. Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf Co-authored-by: Claude <noreply@anthropic.com> * chore(spec): regenerate liveness state counts after the second main sync `gen:liveness-counts` recomputed from the merged ledger: the only stale artifact this round. The total row is the union of both sides — main's objectstack-ai#16784 re-grade of ActionSchema operation/patch (planned -> live, 848 -> 850) plus this branch's experimental `seed.locale` row (exp 5 -> 6, classified 958 -> 959). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6 --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #15080
The liveness ledger's two
plannedrows onActionSchema—operationandpatch— carried anauthorWarnsaying "nothing performs the write yet". The runtime half landed (#15079, via PR #15448), so the premise is gone and the ledger has to say what the tree does.The claim this PR makes, and how it was established
The card's own pin is that every evidence anchor lands on a symbol the merged runtime tree contains. That is necessary and not sufficient — a green
check:livenessproves the ledger is internally consistent, never that the cited code reads the key. So each key was judged separately, by reading the merged code and then by deleting the read and watching the pins fail.operation— read, and read FIRSTpackages/runtime/src/action-execution.ts#isDeclarativeUpdateActionreturn action?.operation === DECLARATIVE_UPDATE_OPERATION;— the executor's own discriminator, deliberately a bare equality with notypeclausepackages/runtime/src/domains/actions.ts#handleActionsRequest/actionsdoor branches on it ahead of thetypeswitch and callsexecuteDeclarativeUpdateActionpackages/runtime/src/action-execution.ts#invokeBusinessActionrun_actiondoor takes the same branch into the same executorpackages/runtime/src/action-execution.ts#isHeadlessInvokableActiontargetnorbodypackages/runtime/src/action-execution.ts#headlessActionTypeErrortypeprescription stays silent for itpackages/runtime/src/action-execution.ts#summarizeActionoperationand forcesrequiresRecordpatch— read, as the base of the write bagpackages/runtime/src/action-execution.ts#declarativeUpdateWriteconst patch = action?.patch;then{ ...patch, ...params }— patch UNDER paramspackages/runtime/src/action-execution.ts#executeDeclarativeUpdateActionupdateof the routed row, under the caller's own execution context; an empty bag is a located 400, not a silent 200packages/runtime/src/domains/actions.tsis deliberately not cited on thepatchrow: the file never namespatchas a word, so the citation would be unanchored by the gate's own key-mention check, and correctly so — the door reaches the key only through the executor.Ablation — the two reads deleted, one at a time
Run at HEAD with the runtime dependency closure built; each leg mutated on disk (injected marker counted, deleted text counted to zero, blob hash compared against the HEAD blob), run, then restored and proved restored by
git diff HEADbeing empty and by the blob hash matching HEAD again.operationisDeclarativeUpdateActionreturnsfalsepatchdeclarativeUpdateWritestops readingaction.patchPins named in both red legs include "writes the patch to the routed record", "merges the static patch UNDER the collected params", "the MCP
run_actiondoor performs the SAME write, with the same identity", and the security pin "the driver call carries the CALLER, andisSystemis absent". Both keys have a real, behaviour-changing reader; neither row is a paper flip.What changed
packages/spec/liveness/action.json—operationandpatchtolive,verifiedAtrestamped, theauthorWarn/authorHintpair dropped on both, evidence re-anchored on the runtime symbols above, notes rewritten to record the flip and its scope.packages/spec/liveness/state-counts.md— regenerated, never hand-edited (check:generated --fix, which proved exactly this one artifact stale and regenerated only it)..changeset/action-declarative-update-ledger-live.md— see the next section.Counts move exactly as the card pinned:
actiongoes fromlive 41, planned 4, dead 3tolive 43, planned 2, dead 3; the table total goes848 / 12to850 / 10.A card premise that measurement falsified: this is NOT
skip-changesetThe card asked for the
skip-changesetlabel and no changeset, on the ground that the change is docs-only. Measured instead of assumed, that is wrong in this repo:packages/spec'spackage.jsonlistslivenessinfiles[], andnpm pack --dry-run --jsonconfirms it: 2073 files in the tarball, 38 of them underliveness/, including both files this PR edits. Positive controldist/present, negative controlscripts/absent.@objectstack/lintresolves@objectstack/spec/package.jsonat runtime and reads that shipped directory to build its author-warning map (resolveLivenessDir/authorWarnedProperties). Measured both ways: on the base commitauthorWarnedProperties('action')is['operation', 'patch']; on this branch it is[].So a published package's behaviour moves — authoring
operation: 'update'+patchno longer draws anos lintfinding. The sole criterion forskip-changesetis that nothing published moves, so a changeset is written and the label is not applied. Flagged for the maintainer rather than decided silently.Gates
Derived on the merged tree with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, every command run with its exit code recorded, then reconciled: 55 derived, 55 run, 0 UNRUN. 54 exit 0.The one that did not:
pnpm check:dual-build-cjs-loadsexits 3 with its ownPREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ ... ⛔ This is NOT a pass: nothing was measured.It wants a whole-repopnpm build; that is CI's run, and this diff contains no source and no build output. Recorded as NOT MEASURED, not as a pass.pnpm lintwas narrowed, and the narrowing is a measurement rather than a skip: eslint's own config declares its population as**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}and eslint itself answers "File ignored because no matching configuration was supplied" for all three of this PR's paths (--format json: 3 results, 0 errors, exit 0) — a.json, a.mdand a.mdare outside the linted population entirely. The config enables no type-aware linting anywhere (noparserOptions.project, no typed rules — stated and measured ateslint.config.mjs:327-328), so this diff cannot move the verdict on any file it does not touch. Measured at12f96c0.Acceptance notes (observed, not filed, not fixed here)
check:generated --fix"refreshesstate-counts.mdand the references pages". Measured: onlystate-counts.mdwas stale.check:docs(content/docs/references/**) stayed green — the docs generator does not read the liveness ledger. Not a defect, just a card wording that overstates the blast radius.packages/spec/liveness/README.md's hand-written "Current state" note foractiondoes not mention this flip. House style records notable flips there. It is not false today and no gate asks for it, and the file is a collision surface with the wave sibling that also writes liveness rows, so it is deliberately left alone. Carrier: the nextaction-ledger PR.Generated by Claude Code